import * as server from "@minecraft/server";
import * as data_format from "../data/format";
import * as data_entity from "../data/entity";
import * as data_story from "../data/story";
import * as power_property from "./power_property";
import * as native_tools from "./native_tools";
import * as math_tools from "./math_tools";
export { Control, Template, PathExecute, MistySeaFissure, RunePowerAttack, RuneClingEntity, ControlEventTrigger, SprayParticleTrigger, };
class Control {
    static inventory = [];
    static canDisplayError = false;
    static execute() {
        if (this.inventory.length == 0)
            return;
        const waitRemove = new Set();
        this.inventory.forEach(plan => {
            plan.timeScore += 1;
            if (plan.timeScore % plan.cooldown != 0)
                return;
            let license = true;
            const remove = () => waitRemove.add(plan.numberId);
            const cease = () => license = false;
            try {
                plan.beforePlanEvent({ plan, remove, cease });
                if (license)
                    plan.afterPlanEvent({ plan, remove });
            }
            catch (error) {
                if (!(error instanceof Error))
                    return;
                const stack = error.stack ?? './';
                const message = JSON.stringify(error.message);
                const intel = 'THE_OTHER_SHORE:' + message + stack;
                if (this.canDisplayError)
                    console.error(intel);
                remove();
            }
            ;
        });
        if (waitRemove.size > 0)
            this.inventory = this.inventory.filter(plan => !waitRemove.has(plan.numberId));
    }
    ;
    constructor() { }
    ;
}
;
class Template {
    className;
    cooldown;
    annex;
    numberId;
    timeScore = 1;
    beforePlanEvent(data) { }
    ;
    afterPlanEvent(data) { }
    ;
    constructor(className, cooldown, annex) {
        this.className = className;
        this.cooldown = cooldown;
        this.annex = annex;
        this.cooldown = Math.floor(cooldown);
        this.numberId = math_tools.NumberID();
    }
    ;
    static Create(className, cooldown, data) {
        if (className == "")
            throw new Error("类名不能为空");
        const plan = new this(className, cooldown, data);
        Control.inventory.push(plan);
        return plan;
    }
    ;
}
;
server.system.runInterval(() => Control.execute());
class PathExecute extends Template {
    pathIndex = 1;
    current = math_tools.Vector.CONSTANT_ZERO;
    direction = math_tools.Vector.CONSTANT_ZERO;
    nodeDistance = 0;
    location_group = [];
    dimension = server.world.getDimension('overworld');
    particle = [];
    on_move;
    on_done;
    offset;
    annex = {};
    speed = 1;
    boundsCounting = 0;
    afterPlanEvent(data) {
        if (this.speed == 1)
            this.executePathPlan(data);
        else
            for (let index = 0; index < this.speed; index++)
                this.executePathPlan(data);
    }
    ;
    executePathPlan(data) {
        if (this.boundsCounting >= 1)
            return;
        const distance = math_tools.Vector.distance(this.location_group[this.pathIndex], this.current);
        const endIndex = this.location_group.length - 1;
        const enable = math_tools.Vector.distance(this.current, this.location_group[endIndex]) < 1;
        const pathAnnexData = {
            dimension: this.dimension,
            location: this.current,
            tick: this.timeScore
        };
        if (this.on_move) {
            const onClose = this.on_move(pathAnnexData);
            if (onClose == false && this.on_done)
                this.on_done(pathAnnexData);
            if (onClose == false)
                return data.remove();
        }
        ;
        if (this.particle.length > 0)
            this.particle.forEach(particle => native_tools.TrySpawnParticle(this.dimension, particle, this.current));
        this.modifyCurrentLocation(this, distance, endIndex);
        if (!enable || this.pathIndex != endIndex)
            return;
        if (this.on_done)
            this.on_done(pathAnnexData);
        this.boundsCounting += 1;
        return data.remove();
    }
    ;
    modifyCurrentLocation(self, distance, endIndex) {
        if (distance < 1 && self.pathIndex != endIndex) {
            self.pathIndex += 1;
            self.direction = math_tools.Vector.subtract(self.location_group[self.pathIndex], self.current);
            self.nodeDistance = math_tools.Vector.distance(self.location_group[self.pathIndex], self.current);
        }
        else {
            self.current.x += (self.direction.x / self.nodeDistance);
            self.current.y += (self.direction.y / self.nodeDistance);
            self.current.z += (self.direction.z / self.nodeDistance);
        }
        ;
    }
    ;
    static prepareShootingPath(source, args, offset) {
        if (!source || !args.shoot)
            return;
        const endPoint = math_tools.Vector.add(args.shoot.start_place, math_tools.Vector.scale(args.shoot.toward, args.shoot.max_distance));
        source.location_group = [args.shoot.start_place, endPoint];
        if (offset)
            source.location_group = source.location_group.map(location => math_tools.Vector.add(location, offset));
        source.current = source.location_group[0];
        source.direction = math_tools.Vector.subtract(source.location_group[1], source.current);
        source.nodeDistance = math_tools.Vector.distance(source.location_group[1], source.current);
    }
    ;
    static prepareDefaultPath(source, offset) {
        const player = source.dimension.getPlayers()[0];
        if (source.location_group.length === 0) {
            source.location_group = [player.location, math_tools.Vector.add(player.location, { x: 10, y: 10, z: 10 })];
        }
        else if (source.location_group.length === 1) {
            source.location_group.push(math_tools.Vector.add(source.location_group[0], { x: 10, y: 10, z: 10 }));
        }
        if (offset)
            source.location_group = source.location_group.map(location => math_tools.Vector.add(location, offset));
        source.current = source.location_group[0];
        source.direction = math_tools.Vector.subtract(source.location_group[1], source.current);
        source.nodeDistance = math_tools.Vector.distance(source.location_group[1], source.current);
    }
    ;
    static Create(className, cooldown, data) {
        if (className == "")
            throw new Error("类名不能为空");
        const plan = new PathExecute(className, cooldown, data);
        const shootToward = () => {
            if (data.shoot) {
                return {
                    shoot: {
                        toward: data.shoot.toward,
                        start_place: data.shoot.start_place,
                        max_distance: Math.floor(data.shoot.max_distance)
                    }
                };
            }
            else
                return {};
        };
        plan.location_group = data.location_group;
        plan.dimension = data.dimension;
        plan.cooldown = Math.floor(data.cooldown);
        plan.particle = data.particles;
        plan.on_move = data.on_move;
        plan.on_done = data.on_done;
        plan.offset = data.offset;
        plan.speed = Math.floor(data.speed);
        plan.annex = shootToward();
        if (data.shoot)
            PathExecute.prepareShootingPath(plan, data, plan.offset);
        if (!data.shoot)
            PathExecute.prepareDefaultPath(plan, plan.offset);
        Control.inventory.push(plan);
        return plan;
    }
    ;
    static CreateForCube(className, data, start, done, multiple = 1) {
        if (className == "")
            throw new Error("类名不能为空");
        const plan = new PathExecute(className, 1, data);
        if (plan.offset)
            start = math_tools.Vector.add(start, plan.offset);
        if (plan.offset)
            done = math_tools.Vector.add(done, plan.offset);
        const distance = math_tools.Vector.distance(done, start);
        const copyStart = math_tools.Vector.copy(start);
        const copyDone = math_tools.Vector.copy(done);
        plan.modifyCurrentLocation = (self) => {
            if (self.current.x != copyDone.x) {
                self.current.x += copyDone.x > self.current.x ? 1 : -1;
            }
            else if (self.current.x == copyDone.x && self.current.z != copyDone.z) {
                self.current.z += copyDone.z > self.current.z ? 1 : -1;
                self.current.x = copyStart.x;
            }
            else if (self.current.x == copyDone.x && self.current.z == copyDone.z && self.current.y != copyDone.y) {
                self.current.y += copyDone.y > self.current.y ? 1 : -1;
                self.current.x = copyStart.x;
                self.current.z = copyStart.z;
            }
            else if (self.current.x == copyDone.x && self.current.z == copyDone.z && self.current.y == copyDone.y) {
                self.pathIndex = 1;
                self.boundsCounting += 2;
            }
            ;
        };
        plan.nodeDistance = math_tools.Vector.distance(done, start);
        plan.direction = math_tools.Vector.subtract(done, start);
        plan.location_group = [start, done];
        plan.dimension = data.dimension;
        plan.cooldown = Math.floor(data.cooldown);
        plan.particle = data.particles;
        plan.on_move = data.on_move;
        plan.on_done = data.on_done;
        plan.offset = data.offset;
        plan.current = start;
        plan.speed = Math.floor((distance - 1) * multiple) + 1;
        Control.inventory.push(plan);
        return plan;
    }
    ;
    static CreateForFrame(className, data, start, done) {
        if (className == "")
            throw new Error("类名不能为空");
        const plan = new PathExecute(className, 1, data);
        const vector = math_tools.Vector.subtract(done, start);
        const distance = math_tools.Vector.distance(done, start);
        const location_group = [];
        if (vector.y != 0 && vector.x != 0 && vector.z != 0) {
            location_group.push(math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }), math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: 0 }), math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }), math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO), math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }), math_tools.Vector.add(start, { x: 0, y: vector.y, z: vector.z }), math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }), math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO), math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }), math_tools.Vector.add(start, { x: vector.x, y: 0, z: vector.z }), math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }), math_tools.Vector.add(start, { x: 0, y: vector.y, z: vector.z }), math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: vector.z }), math_tools.Vector.add(start, { x: vector.x, y: 0, z: vector.z }), math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }), math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: 0 }), math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: vector.z }));
        }
        else if (vector.y == 0 && vector.x != 0 && vector.z != 0) {
            location_group.push(math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }), math_tools.Vector.add(start, { x: vector.x, y: 0, z: vector.z }), math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }), math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO));
        }
        else if (vector.y != 0 && vector.x == 0 && vector.z != 0) {
            location_group.push(math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }), math_tools.Vector.add(start, { x: 0, y: vector.y, z: vector.z }), math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }), math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO));
        }
        else if (vector.y != 0 && vector.x != 0 && vector.z == 0) {
            location_group.push(math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }), math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: 0 }), math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }), math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO));
        }
        ;
        plan.location_group = [start, ...location_group];
        plan.dimension = data.dimension;
        plan.cooldown = Math.floor(data.cooldown);
        plan.particle = data.particles;
        plan.on_move = data.on_move;
        plan.on_done = data.on_done;
        plan.offset = data.offset;
        plan.speed = distance;
        PathExecute.prepareDefaultPath(plan, plan.offset);
        Control.inventory.push(plan);
        return plan;
    }
    ;
}
;
class MistySeaFissure extends Template {
    afterPlanEvent(data) {
        if (!this.annex.dimension_group)
            return data.remove();
        if (!this.annex.location_group)
            return data.remove();
        ;
        const showDimension = this.annex.dimension_group[0];
        const showLocation = this.annex.location_group[0];
        const pointLocation = this.annex.location_group[1];
        const pointDimension = this.annex.dimension_group[1];
        native_tools.TrySpawnParticle(showDimension, 'constant:the_cracks_of_the_misty_sea', showLocation);
        showDimension.getEntities({ location: showLocation, maxDistance: 3 }).forEach(entity => entity.tryTeleport(pointLocation, { dimension: pointDimension }));
    }
}
;
class RunePowerAttack extends Template {
    DamageOptions(self) {
        return {
            cause: server.EntityDamageCause[this.annex.hurt_Type ?? 'entityAttack'],
            damagingEntity: self
        };
    }
    ;
    afterPlanEvent(data) {
        const target = this.annex.target;
        const erupt = this.annex.erupt;
        const self = this.annex.self;
        if (!self || !target || !target.isValid() || !target.getComponent('health') || erupt == undefined)
            return data.remove();
        const self_data = power_property.MergeProperty(power_property.GetProperty(self), this.annex.hurt_data ?? {});
        const target_data = power_property.GetProperty(target);
        const options = this.DamageOptions(self);
        const fusion = RuneElementalReactions(target, target_data.add_rune, self_data.self_rune);
        const improve = power_property.MergeProperty(self_data, { double_damage: fusion + self_data.double_damage });
        const damage = RuneElementalDamage(improve, erupt);
        if (self_data.self_rune !== target_data.self_rune)
            target.applyDamage(damage, options);
        else
            ElementalResistance(target, options, damage, data_entity.rune_resistance);
        power_property.SetProperty(target, { add_rune: self_data.self_rune });
        power_property.SetProperty(self, data_entity.reset_battle_data);
        RuneClingEntity.CreateToken(target);
        return data.remove();
    }
    ;
    static BriefCreate(self, target, erupt = false, hurt_data) {
        return this.Create(self.typeId, 1, { hurt_data, self, target, erupt });
    }
    ;
}
;
class RuneClingEntity extends Template {
    static tokenGroup = [];
    static CreateToken(source) {
        const test = RuneClingEntity.tokenGroup.filter(token => token.id === source.id).length;
        if (test === 0)
            RuneClingEntity.tokenGroup.push({ id: source.id, wait: 100 });
        else {
            RuneClingEntity.tokenGroup = RuneClingEntity.tokenGroup.filter(token => token.id !== source.id);
            RuneClingEntity.tokenGroup.push({ id: source.id, wait: 100 });
        }
        return test === 0;
    }
    ;
    afterPlanEvent() {
        const reset = RuneClingEntity.tokenGroup.filter(token => token.wait <= 0);
        const valid = RuneClingEntity.tokenGroup.filter(token => token.wait > 0);
        RuneClingEntity.tokenGroup = RuneClingEntity.tokenGroup.filter(token => token.wait > 0);
        RuneClingEntity.tokenGroup.forEach(token => token.wait -= 1);
        reset.forEach(token => {
            const entity = server.world.getEntity(token.id);
            if (!entity)
                return;
            power_property.AlterProperty(entity, { add_rune: 'rune_void' });
        });
        valid.forEach(token => {
            const entity = server.world.getEntity(token.id);
            if (!entity)
                return;
            const molang = new server.MolangVariableMap();
            const state = power_property.GetProperty(entity);
            const dimension = entity.dimension;
            const colour = data_story.getRuneColor(state.self_rune);
            molang.setFloat('variable.scale', 0.35);
            molang.setColorRGB('variable.color', colour);
            molang.setVector3('variable.offset', { x: 0, y: 1.5, z: 0 });
            native_tools.TrySpawnParticle(dimension, `scripts:fusion_${state.add_rune}`, entity.getHeadLocation(), molang);
        });
    }
    ;
    static BriefCreate(nameTag) {
        return this.Create(nameTag, 1, {});
    }
    ;
}
;
class ControlEventTrigger extends Template {
    static tokenGroup = [];
    static EntityToken(type, source, wait) {
        const test = ControlEventTrigger.tokenGroup.filter(token => token.type === type && token.id === source.id).length;
        if (test === 0)
            ControlEventTrigger.tokenGroup.push({ type, id: source.id, wait });
        return test === 0;
    }
    ;
    static BlockToken(type, block, wait) {
        const dimension = block.dimension.id.split(/:/);
        const blockID = `${dimension[0]}.${block.x}.${block.y}.${block.z}.${dimension[1]}`;
        const test = ControlEventTrigger.tokenGroup.filter(token => token.type === type && token.id === blockID).length;
        if (test === 0)
            ControlEventTrigger.tokenGroup.push({ type, id: blockID, wait });
        return test === 0;
    }
    ;
    afterPlanEvent() {
        ControlEventTrigger.tokenGroup = ControlEventTrigger.tokenGroup.filter(token => token.wait > 0);
        ControlEventTrigger.tokenGroup.forEach(token => token.wait -= 1);
    }
    ;
    static BriefCreate(nameTag) {
        return this.Create(nameTag, 1, {});
    }
    ;
}
;
class SprayParticleTrigger extends Template {
    afterPlanEvent(data) {
        const dimension = (this.annex.dimension_group ?? [server.world.getAllPlayers()[0].dimension])[0];
        const location = (this.annex.location_group ?? [server.world.getAllPlayers()[0].location])[0];
        const size = this.annex.index ?? 1;
        const molangGroup = [
            new server.MolangVariableMap(),
            new server.MolangVariableMap(),
            new server.MolangVariableMap(),
            new server.MolangVariableMap()
        ];
        molangGroup[0].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_EAST);
        molangGroup[1].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_WEST);
        molangGroup[2].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_SOUTH);
        molangGroup[3].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_NORTH);
        server.system.runTimeout(() => {
            native_tools.TrySpawnParticle(dimension, 'scripts:spray_main_ripple', location, molangGroup[0]);
            molangGroup.forEach(map => native_tools.TrySpawnParticle(dimension, 'scripts:spray_large_phase1', location, map));
        }, 1);
        server.system.runTimeout(() => {
            native_tools.TrySpawnParticle(dimension, 'scripts:spray_splash', location, molangGroup[0]);
            native_tools.TrySpawnParticle(dimension, 'scripts:spray_extend_ripple', location, molangGroup[0]);
            molangGroup.forEach(map => native_tools.TrySpawnParticle(dimension, 'scripts:spray_large_phase2', location, map));
        }, 6);
        return data.remove();
    }
    ;
    static BriefCreate(nameTag, location, dimension) {
        return this.Create(nameTag, 1, { location_group: [location], dimension_group: [dimension] });
    }
}
;
function RuneElementalReactions(target, old, add) {
    const getOldValue = data_format.RUNE_COUNT[old];
    const getAddValue = data_format.RUNE_COUNT[add];
    if (!target)
        return 0;
    const result = data_entity.rune_fusion.get(getOldValue + getAddValue);
    if (!result)
        return 0;
    if (result.event)
        result.event(target);
    return result.double;
}
;
function ElementalResistance(target, options, damage, resistance) {
    native_tools.TrySpawnParticle(target.dimension, 'constant:rune_resistance', target.getHeadLocation());
    target.dimension.playSound('random.glass', target.location);
    target.applyDamage(damage * (1 - resistance), options);
}
;
function RuneElementalDamage(data, erupt) {
    const baseValue = data.basic_attack + data.raise_basic_attack;
    const eruptValue = (data.erupt_hurt + data.raise_erupt_hurt) / 100;
    const eruptBefore = baseValue * data.double_damage;
    const eruptAfter = (baseValue * eruptValue) * data.double_damage;
    return erupt ? (eruptAfter + data.damage_increase) : (eruptBefore + data.damage_increase);
}
;
;
;
;
;
;
;
;
;
;
;
;
